﻿using System.Text;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using DatabaseGroup.SSIS.SourceAdapters.Parser;

namespace DatabaseGroup.UnitTests.SSIS.SourceAdapters.Parser
{
    [TestClass()]
    public class FileReaderTest
    {
        private const string _dummyText = @"ABCDFGHJK1234567%^&*()";
        private const string _tempFileName = "temp.txt";
        private TestContext _testContextInstance;

        public TestContext TestContext
        {
            get
            {
                return _testContextInstance;
            }
            set
            {
                _testContextInstance = value;
            }
        }

        /// <summary>
        /// Create a dummy text file with some dummy data
        /// </summary>
        /// <param name="testContext">The running test context</param>
        [ClassInitialize()]
        public static void TestClassInitialize(TestContext testContext)
        {
            FileStream stream = new FileStream(GetTempFileName(), FileMode.Create, FileAccess.ReadWrite);
            StreamWriter writer = new StreamWriter(stream, Encoding.Unicode);

            writer.Write(_dummyText);

            writer.Close();
            stream.Close();
        }

        /// <summary>
        /// Remove the dummy test file
        /// </summary>
        [ClassCleanup()]
        public static void TestClassCleanup()
        {
            File.Delete(GetTempFileName());
        }

        [TestMethod()]
        [ExpectedException(typeof(System.ArgumentNullException), "Expected ArgumentNullException when called with empty 1st arg")]
        public void FileReader_ConstructorWithEmpty1stArg_ThrowArgumentNullException()
        {
            IFileReader target = new FileReader(string.Empty, Encoding.Unicode);
        }

        [TestMethod()]
        [ExpectedException(typeof(System.ArgumentException), "Expected ArgumentException when called with invalid filename")]
        public void FileReader_ConstructorWithInvalidFilenameArg_ThrowArgumentException()
        {
            IFileReader target = new FileReader("@#<->@#\\BadFileName", Encoding.Unicode);
        }

        [TestMethod()]
        [ExpectedException(typeof(System.ArgumentNullException), "Expected ArgumentNullException when called with null 2nd arg")]
        public void FileReader_ConstructorWithNull2ndArg_ThrowArgumentNullException()
        {
            IFileReader target = new FileReader(GetTempFileName(), null);
        }

        [TestMethod()]
        public void GetNextChar_ReadCharsFromFile_ReturnExpectedChars()
        {
            FileReader target = new FileReader(GetTempFileName(), Encoding.Unicode);

            for (int i = 0; i < _dummyText.Length; i++)
            {
                Assert.AreEqual<char>(_dummyText[i], target.GetNextChar(), "Expected char was not read from the file");
            }

            target.Close();
        }

        [TestMethod()]
        public void GetNextChar_ReadAllDataInFile_ReturnWithFileEOFState()
        {
            FileReader target = new FileReader(GetTempFileName(), Encoding.Unicode);

            for (int i = 0; i < _dummyText.Length; i++)
            {
                target.GetNextChar();
            }

            target.GetNextChar();
            Assert.AreEqual<bool>(true, target.IsEOF, "Expected file reader to be in EOF state after reading all avaliable data");

            target.Close();
        }

        [TestMethod()]
        public void CountBOMBytes_EmptyByteOrderMark_ReturnCountOfZero()
        {
            byte[] byteBuffer = new byte[4];
            Assert.AreEqual<int>(0, FileReader.CountBOMBytes(byteBuffer), "Expected zero BOM bytes to be recognised");
        }

        [TestMethod()]
        public void CountBOMBytes_UTF16BigEndianByteOrderMark_ReturnCountOfTwo()
        {
            byte[] byteBuffer = new byte[4];
            byteBuffer[0] = 0xFE;
            byteBuffer[1] = 0xFF;
            Assert.AreEqual<int>(2, FileReader.CountBOMBytes(byteBuffer), "Expected 2 BOM bytes to be recognised");
        }

        [TestMethod()]
        public void CountBOMBytes_UTF16LittleEndianByteOrderMark_ReturnCountOfTwo()
        {
            byte[] byteBuffer = new byte[4];
            byteBuffer[0] = 0xFF;
            byteBuffer[1] = 0xFE;
            Assert.AreEqual<int>(2, FileReader.CountBOMBytes(byteBuffer), "Expected 2 BOM bytes to be recognised");
        }

        [TestMethod()]
        public void CountBOMBytes_UTF8ByteOrderMark_ReturnCountOfThree()
        {
            byte[] byteBuffer = new byte[4];
            byteBuffer[0] = 0xEF;
            byteBuffer[1] = 0xBB;
            byteBuffer[2] = 0xBF;
            Assert.AreEqual<int>(3, FileReader.CountBOMBytes(byteBuffer), "Expected 3 BOM bytes to be recognised");
        }

        [TestMethod()]
        public void CountBOMBytes_UTF32BigEndianByteOrderMark_ReturnCountOfFour()
        {
            byte[] byteBuffer = new byte[4];
            byteBuffer[0] = 0;
            byteBuffer[1] = 0;
            byteBuffer[2] = 0xFE;
            byteBuffer[3] = 0xFF;
            Assert.AreEqual<int>(4, FileReader.CountBOMBytes(byteBuffer), "Expected 4 BOM bytes to be recognised");
        }

        [TestMethod()]
        public void CountBOMBytes_UTF32UnrecognisedByteOrderMark_ReturnCountOfZero()
        {
            byte[] byteBuffer = new byte[4];
            byteBuffer[0] = 0;
            byteBuffer[1] = 0;
            byteBuffer[2] = 0x19;
            byteBuffer[3] = 0x39;
            Assert.AreEqual<int>(0, FileReader.CountBOMBytes(byteBuffer), "Expected zero BOM bytes to be recognised");
        }

        [TestMethod()]
        public void CountBOMBytes_1ByteUnrecognisedByteOrderMark_ReturnCountOfZero()
        {
            byte[] byteBuffer = new byte[1];
            byteBuffer[0] = 0;
            Assert.AreEqual<int>(0, FileReader.CountBOMBytes(byteBuffer), "Expected zero BOM bytes to be recognised");
        }

        [TestMethod()]
        public void CountBOMBytes_2ByteUnrecognisedByteOrderMark_ReturnCountOfZero()
        {
            byte[] byteBuffer = new byte[2];
            byteBuffer[0] = 0;
            byteBuffer[1] = 0;
            Assert.AreEqual<int>(0, FileReader.CountBOMBytes(byteBuffer), "Expected zero BOM bytes to be recognised");
        }

        [TestMethod()]
        public void CountBOMBytes_3ByteUnrecognisedByteOrderMark_ReturnCountOfZero()
        {
            byte[] byteBuffer = new byte[3];
            byteBuffer[0] = 0;
            byteBuffer[1] = 0;
            byteBuffer[2] = 0;
            Assert.AreEqual<int>(0, FileReader.CountBOMBytes(byteBuffer), "Expected zero BOM bytes to be recognised");
        }

        /// <summary>
        /// Initializes a new IFileReader based on the string of text
        /// </summary>
        /// <param name="text">The string of text to use as the source data</param>
        /// <returns>The initialized IFileReader</returns>
        internal static IFileReader GetReader(string text)
        {
            IFileReader target = new FileReaderTestImpl(text);

            return target;
        }

        /// <summary>
        /// Creates a temp filename with path for testing
        /// </summary>
        /// <returns>The complete temp file path</returns>
        private static string GetTempFileName()
        {
            return Path.GetTempPath() + _tempFileName;
        }
    }
}
